/* components */
// @ts-ignore
import LayX from 'publink-layx'
/* enum */
import ComponentNameEnum from '@model/enum/ComponentNameEnum'
/* scss */
import '@src/component/common/BaseWindow/BaseWindow.scss'
/* vue */
import VC from '@model/VC'
import { Component, Emit, Prop, PropSync, Watch } from 'vue-property-decorator'
import { CreateElement, VNode } from 'vue'
/* util */
import { randomString } from 'pub-bbx-utils'
import Log from '@src/util/log.ts'
import { isNumber } from '@src/util/type'

export enum BaseWindowSizeEnum {
  Mini = 'mini',
  Smaller = 'smaller',
  Small = 'small',
  Medium = 'medium',
  Large = 'large',
  Larger = 'larger'
}

enum BaseWindowEmitEventNameEnum {
  Close = 'close',
  Closed = 'closed',
  Cancel = 'cancel',
  GetID = 'getId',
  ToggleFullscreen = 'toggleFullscreen',
}

type BaseWindowSizeData = {
  minHeight: string | number
  minWidth: string | number
}

const BaseWindowSizeMap = {
  [BaseWindowSizeEnum.Mini]:       { minWidth: 500, minHeight: 200 },
  [BaseWindowSizeEnum.Smaller]:    { minWidth: 600, minHeight: 300 },
  [BaseWindowSizeEnum.Small]:      { minWidth: 600, minHeight: 400 },
  [BaseWindowSizeEnum.Medium]:     { minWidth: 800, minHeight: 500 },
  [BaseWindowSizeEnum.Large]:      { minWidth: 900, minHeight: 650 },
  [BaseWindowSizeEnum.Larger]:     { minWidth: '98%', minHeight: '98%' }
}
const DefaultClassName: string = 'base-window-main'
const PositionTopNumber: number = 100

let BaseWindowIds: string[] = []

@Component({ 
  name: ComponentNameEnum.BaseWindow
})
export default class BaseWindow extends VC {
  
  /* 主体类名 */
  @Prop() readonly bodyClass: string | undefined
  /* 类名 */
  @Prop() readonly className: string | string[] | undefined
  /* 窗口的配置数据 */
  @Prop() readonly options: Record<string, any> | undefined
  /* 尺寸大小 */
  @Prop({ default: BaseWindowSizeEnum.Smaller }) readonly size: BaseWindowSizeEnum | undefined
  /* 窗口是否闪烁 */
  @Prop({ default: false }) readonly flicker: boolean | undefined
  /* 标题 */
  @Prop() readonly title: string | undefined
  /* 是否总是置顶 */
  @Prop({ default: false }) readonly top: boolean | undefined
  /* 是否唯一 */
  @Prop({ default: true }) readonly unique: boolean | undefined
  /* 大小配置 */
  @Prop() readonly sizeData: Record<string, number> | undefined
  
  /* 是否显示窗口的状态 */
  @PropSync('show', { type: Boolean }) showBaseWindow: boolean | undefined
  
  /* 窗口id */
  private layxWindowId: string = ''
  
  /* 当前窗口是否是唯一的 */
  get isUniqueWindow(): boolean {
    return Boolean(this.unique)
  }
  
  @Watch('show', { immediate: true })
  onShowChangedHandler(newValue: boolean): void {
    if (newValue) {
      return this.showWindow(this.options || {})
    }
    
    if (this.isUniqueWindow) {
      return this.closeAllWindows()
    }
    
    this.closeCurrentWindow()
  }
  
  @Watch('flicker', { immediate: true })
  onFlickerChangedHandler(newValue: boolean): void {
    if (newValue) {
      return this.flickerWindow()
    }
  }
  
  public emitShowChangedHandler(value: boolean = true) {
    this.showBaseWindow = value
  }
  
  @Emit(BaseWindowEmitEventNameEnum.Close)
  public emitCloseEventHandler() {}
  
  @Emit(BaseWindowEmitEventNameEnum.Closed)
  public emitClosedEventHandler() {}

  @Emit(BaseWindowEmitEventNameEnum.Cancel)
  public emitCancelEventHandler() {}
  
  @Emit(BaseWindowEmitEventNameEnum.GetID)
  public emitGetIdEventHandler() {
    return this.layxWindowId
  }

  @Emit(BaseWindowEmitEventNameEnum.ToggleFullscreen)
  public emitFullscreenEventHandler(isFullscreen: boolean) {}
  
  private buildEvent() {
    return {
      onload: {
        before: this.onLoadBeforeEventHandler,
        after: this.onLoadAfterEventHandler
      },
      ondestroy: {
        before: this.onDestoryBeforeEventHandler,
        after: this.onDestoryAfterEventHandler
      },
      onrestore: {
        after: this.onMinAfterEventHandler
      },
      onmax: {
        after: this.onMaxAfterEventHandler
      },
    }
  }
  
  /**
   * @description 关闭窗口
   * @param {String} id 窗口id
   * @return {void}
   */  
  public static closeWindow(id: string): void {
    LayX.destroy(id)
    Log.warn('destroy', this.closeWindow.name)
  }
  
  /**
   * @description 关闭窗口
   * @param {String} id 窗口id
   * @return {void}
   */  
  private closeWindow(id: string = this.layxWindowId): void {
    LayX.destroy(id)
    Log.warn('destroy', this.closeWindow.name)
  }


  /**
   * @description 还原window
   * @param {String} id 窗口id
   * @return {void}
   */  
   private restoreWindow(id: string = this.layxWindowId): void {
    LayX.restore(id)
    Log.warn('restore', this.closeWindow.name)
  }
  
  /**
   * @description 关闭当前窗口
   * @return {void}
   */  
  private closeCurrentWindow(): void {
    LayX.destroy(this.layxWindowId)
    Log.warn('destroy', this.closeCurrentWindow.name)
  }
  
  /**
   * @description 关闭所有窗口
   * @return {void}
   */  
  private closeAllWindows(): void {
    LayX.destroyAll()
    Log.warn('destroyAll', this.closeAllWindows.name)
  }
  
  public static closeAllWindows(): void {
    LayX.destroyAll()
    Log.warn('destroyAll', this.closeAllWindows.name)
  }
  
  /**
   * @description 闪烁窗口
   * @return {void}
   */ 
  private flickerWindow(): void {
    LayX.flicker(this.layxWindowId)
    Log.warn('flicker', this.flickerWindow.name)
  }
  
  public static getBaseWindowIds(): string[] {
    return BaseWindowIds
  }
  
  private getBaseWindowIds(): string[] {
    return BaseWindowIds
  }
  
  private getBaseWindowSizeDataObject(size: BaseWindowSizeEnum) {
    
    if (this.sizeData) {
      return this.sizeData
    }
    
    return BaseWindowSizeMap[size]
  }
  
  private getBaseWindowSizeData(size: BaseWindowSizeEnum) {
    
    const BaseWindowSizeData = this.getBaseWindowSizeDataObject(size)
    
    const WindowInnerHeight = window.innerHeight
    let minHeight = BaseWindowSizeData.minHeight
    
    if (isNumber(BaseWindowSizeData.minHeight) && BaseWindowSizeData.minHeight > WindowInnerHeight) {
      minHeight = WindowInnerHeight - 100
    }
    
    return {
      minWidth: BaseWindowSizeData.minWidth,
      minHeight
    }
  }
  
  private genLayxWindowId(options: Record<string, any>): string {
    
    let id: string = options?.id || `window-${randomString(4)}-${randomString(4)}`
    
    this.layxWindowId = id
    BaseWindowIds.push(id)
    
    return id
  }
  
  private getPosition(baseWindowSizeData: BaseWindowSizeData): string | Array<string | number> {
    if (this.size === BaseWindowSizeEnum.Large || this.size === BaseWindowSizeEnum.Larger) {
      return 'ct'
    }
    
    const WindowInnerHeight = window.innerHeight
    if (
      isNumber(baseWindowSizeData.minHeight)
      && (baseWindowSizeData.minHeight as number + PositionTopNumber * 2) < WindowInnerHeight
    ) {
      return [100, 'tc']
    }
    
    return 'ct'
  }
  
  private onLoadBeforeEventHandler(layxWindow: HTMLDivElement, winform: any) {
    
  }
  
  private onLoadAfterEventHandler(layxWindow: HTMLDivElement, winform: any) {
    
  }
  
  private onDestoryBeforeEventHandler(layxWindow: HTMLDivElement, winform: any) {
    this.emitCloseEventHandler()
    this.emitShowChangedHandler(false)
  }
  
  private onDestoryAfterEventHandler(layxWindow: HTMLDivElement | undefined, winform: any) {
    this.emitClosedEventHandler()
    this.emitShowChangedHandler(false)
  }

  private onMinAfterEventHandler(layxWindow: HTMLDivElement | undefined, winform: any) {
    this.emitFullscreenEventHandler(false)
  }
  private onMaxAfterEventHandler(layxWindow: HTMLDivElement | undefined, winform: any) {
    this.emitFullscreenEventHandler(true)
  }
  
  private openLayxWindow(options: Record<string, any>) {
    const baseWindowSizeData: BaseWindowSizeData = this.getBaseWindowSizeData(this.size || BaseWindowSizeEnum.Smaller)
    
    let moveLimit = options.moveLimit || {};
    // 打开窗口
    LayX.open({
      storeStatus: options.storeStatus ?? false,
      alwaysOnTop: Boolean(this.top),
      className: this.className || DefaultClassName,
      id: this.genLayxWindowId(options),
      title: options.title || this.title || '',
      type: 'html',
      content: this.$refs.BaseWindowContent,
      /** 
       * 如果content传入的是DOM对象并且DOM对象包含触发事件，需设置false采用原对象填充content内容。
       * 设置为true拷贝一份填充content内容，但所有事件失效。
       */
      cloneElementContent: false,
      event: this.buildEvent(),
      /**
       * 支持传入百分比，百分比是相对于浏览器可视区域高度。
       * 如果宽度小于最小高度，则高度强制性设置为最小高度。最小高度作用于拖曳调整窗口大小时。  
       */
      minHeight: baseWindowSizeData.minHeight,
      height: baseWindowSizeData.minHeight,
      /**
       * 支持传入百分比，百分比是相对于浏览器可视区域宽度。
       * 如果宽度小于最小宽度，则宽度强制性设置为最小宽度。最小宽度作用于拖曳调整窗口大小时。  
       */
      minWidth: baseWindowSizeData.minWidth,
      width: baseWindowSizeData.minWidth,
      /** 
       * 窗口位置
       * ct：正中间
       * lt：左上角
       * rt：右上角
       * lb：左下角
       * rb：右下角
       * lc：左中间
       * rc：右中间
       * tc：上中间
       * bc：下中间
       * 也可以传入 [100,200] 数组类型，组合数组类型 ['tc',20]
      */
      position: this.getPosition(baseWindowSizeData),
      // 设置拖曳调整大小限制项
      resizeLimit: {
        t: false,   // 是否限制上边拖曳大小，false不限制
        r: false,   // 是否限制右边拖曳大小，false不限制
        b: false,   // 是否限制下边拖曳大小，false不限制
        l: false,   // 是否限制左边拖曳大小，false不限制
        lt: false,  // 是否限制左上边拖曳大小，false不限制
        rt: false,  // 是否限制右上边拖曳大小，false不限制
        lb: false,  // 是否限制左下边拖曳大小，false不限制
        rb: false   // 是否限制右下边拖曳大小，false不限制
      },
      shadable: !!options.shadable, // 遮罩
      minMenu: options.minMenu === undefined || !!options.minMenu , // 最小化 按钮
      maxMenu: options.maxMenu === undefined || !!options.maxMenu , // 最大化 按钮
      moveLimit: {
        leftOut: true, // 是否允许左边拖出，true允许
        rightOut: true, // 是否允许右边拖出，true允许
        topOut: true, // 是否允许上边拖出，true允许，此设置不管是false还是true，窗口都不能拖出窗体
        bottomOut: true, // 是否允许下边拖出，true允许
        ...moveLimit,
      },
    })
    
    this.emitGetIdEventHandler()
  }
  
  private showWindow(options: Record<string, any>) {
    
    if (this.isUniqueWindow) {
      this.closeAllWindows()
    }
    
    this.$nextTick(() => {
      this.openLayxWindow(options)
    })
  }
  
  private outsideShowWindow(options: Record<string, any>) {
    this.showWindow(options)
  }
  
  private renderFooter(): VNode[] {
    return this.$slots.footer || []
  }
  
  render(h: CreateElement) {
    if (!this.showBaseWindow) return null
    
    return (
      <div class={ComponentNameEnum.BaseWindow}>
        <transition name='modal-fade'>
          <div class='base-window-content' ref='BaseWindowContent'>
            <div class='base-window-content-body'>
              { this.$slots.default }
            </div>
            <div class='base-window-content-footer'>
              { this.renderFooter() }
            </div>
          </div>
        </transition>
      </div>
    )
  }
}

